/*  This file is part of -_-.

    -_- is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    -_- is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with -_-.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2012-2013 Oliver Katz */

#include "codeserver.h"
#include "../../common/debug.h"

namespace sleepyface
{
	namespace opal
	{
		using namespace vm;

		void CodeServer::updateImportPath()
		{
			importPath.clear();
			vector<Token> toks = importPathLexer.lex(_cfg["import-path"], "-import-path-");
			for (vector<Token>::iterator i = toks.begin(); i != toks.end(); i++)
				importPath.push_back(i->get());
		}

		void CodeServer::updateBoolCfg()
		{
			for (map<string, bool>::iterator i = boolCfg.begin(); i != boolCfg.end(); i++)
			{
				if (_cfg[i->first].compare("1") == 0)
					boolCfg[i->first] = true;
				else if (_cfg[i->first].compare("0") == 0)
					boolCfg[i->first] = false;
				else
				{
					Z_WARNING(i->first << "can either be a 1 or a 0");
				}
			}
		}

		void CodeServer::addBoolCfg(string k)
		{
			boolCfg[k] = false;
		}

		bool CodeServer::isInteger(string s)
		{
			for (string::iterator i = s.begin(); i != s.end(); i++)
				if (!isdigit(*i))
					return false;
			return true;
		}

		bool CodeServer::isDouble(string s)
		{
			for (string::iterator i = s.begin(); i != s.end(); i++)
				if (!(isdigit(*i) || *i == '.'))
					return false;
			return (s.find(".") != string::npos);
		}

		bool CodeServer::isAddress(string s)
		{
			if (s[0] != '0')
				return false;
			if (s[1] != 'x')
				return false;
			string p = "0123456789abcdefABCDEF";
			for (int i = 2; i < s.size(); i++)
				if (p.find(s[i]) == string::npos)
					return false;
			return true;
		}

		bool CodeServer::isString(string s)
		{
			return ((s.front() == '\'' && s.back() == '\'') || (s.front() == '"' && s.back() == '"'));
		}

		bool CodeServer::isSymbol(string s)
		{
			if (!isalpha(s.back()))
				return false;
			for (string::iterator i = s.begin(); i != s.end(); i++)
				if (!isalnum(*i) && string("_.-/").find(*i) == string::npos)
					return false;
			return true;
		}

		bool CodeServer::isValue(string s)
		{
			return (isInteger(s) || isDouble(s) || isString(s) || isSymbol(s) || isAddress(s));
		}

		bool CodeServer::isFunction(string s)
		{
			if (protos.find(s) != protos.end())
				return true;
			return false;
		}

		bool CodeServer::isVariable(string s)
		{
			if (mutables.find(s) != mutables.end())
				return true;
			return false;
		}

		bool CodeServer::isOperation(string s)
		{
			for (vector<string>::iterator i = ops.begin(); i != ops.end(); i++)
				if (i->compare(s) == 0)
					return true;
			return false;
		}

		vm::LiteralType CodeServer::proofOfType(vm::FunctionalNode n)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], n.display());

			if (n.getType() == vm::FunctionalNode::_value)
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], n.getValue().getType().display() << " (retrieved from value)");
				return n.getValue().getType();
			}
			else if (n.getType() == vm::FunctionalNode::_arith)
			{
				if (proofOfType(n.getOperation().getLeft()).get() == LiteralType::_double || proofOfType(n.getOperation().getRight()).get() == LiteralType::_double)
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "double (retrieved from operation)");
					return LiteralType(LiteralType::_double);
				}
				else
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "int (retrieved from operation)");
					return LiteralType(LiteralType::_int);
				}
			}
			else if (n.getType() == vm::FunctionalNode::_call)
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], protos[n.getCall().getName()].getReturnType().display() << " (retrieved from prototype)");
				if (protos.find(n.getCall().getName()) != protos.end())
					return protos[n.getCall().getName()].getReturnType();
			}

			Z_DEBUG_OUT(boolCfg["debug-compilation"], "int (by default)");
			return LiteralType(LiteralType::_int);
		}

		AST CodeServer::parseAST(string line, string file)
		{
			vector<Token> toks = lexer.lex(line, file);
			if (boolCfg["debug-parsing"])
			{
				stringstream ss;
				for (vector<Token>::iterator i = toks.begin(); i != toks.end(); i++)
				{
					ss << "'" << i->get() << "' ";
				}
				Z_DEBUG_IN(true, ss.str());
			}
			AST astNoImports = parser.parse(toks, file);
			Z_DEBUG_OUT(boolCfg["debug-parsing"], astNoImports.display());

			if (astNoImports.get().get().compare("#") == 0)
			{
				if (astNoImports.getBranches().size() == 2)
				{
					if (astNoImports.getBranches()[0].isLeaf())
						directMacros[astNoImports.getBranches()[0].get().get()] = astNoImports.getBranches()[1];
					else
					{
						functionalMacros[astNoImports.getBranches()[0].get().get()] = FunctionalMacro(astNoImports.getBranches()[0].get().get(), astNoImports.getBranches()[1]);
						for (vector<AST>::iterator i = astNoImports.getBranches()[0].getBranches().begin(); i != astNoImports.getBranches()[0].getBranches().end(); i++)
						{
							if (!i->isLeaf())
								error(ast.get(), file, _cfg["error-improper-macro"]);
							functionalMacros[astNoImports.getBranches()[0].get().get()].addArgument(i->get().get());
						}
					}
				}
				else
					error(ast.get(), file, _cfg["error-improper-macro"]);
			}

			AST astMacros = evaluateMacros(astNoImports, file);

			return handleAllImports(astMacros, "import", lexer, parser, importPath, file);
		}

		AST CodeServer::evaluateMacros(AST ast, string file)
		{
			if (ast.isLeaf() && directMacros.find(ast.get().get()) != directMacros.end())
				return directMacros[ast.get().get()];
			else if (functionalMacros.find(ast.get().get()) != functionalMacros.end())
			{
				if (ast.getBranches().size() != functionalMacros[ast.get().get()].size())
				{
					error(ast.get(), file, _cfg["error-macro-arguments"]);
					note("expression argument size", ast.getBranches().size(), file);
					note("macro argument size", functionalMacros[ast.get().get()].size(), file);
					return ast;
				}

				map<string, AST> args;
				for (int i = 0; i < functionalMacros[ast.get().get()].size(); i++)
					args[functionalMacros[ast.get().get()].getArgument(i)] = ast.getBranches()[i];

				return functionalMacros[ast.get().get()].evaluate(ast, args);
			}
			else
				return ast;
		}

		vm::LiteralType CodeServer::compileAtomicType(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (!ast.isLeaf())
			{
				error(ast.get(), file, _cfg["error-atomic-type-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return LiteralType();
			}

			LiteralType rtn;
			
			if (ast.get().get().compare("char") == 0)
				rtn = LiteralType(LiteralType::_char);
			else if (ast.get().get().compare("i8") == 0)
				rtn = LiteralType(LiteralType::_char);
			else if (ast.get().get().compare("int") == 0)
				rtn = LiteralType(LiteralType::_int);
			else if (ast.get().get().compare("i32") == 0)
				rtn = LiteralType(LiteralType::_int);
			else if (ast.get().get().compare("double") == 0)
				rtn = LiteralType(LiteralType::_double);
			else if (ast.get().get().compare("f32") == 0)
				rtn = LiteralType(LiteralType::_double);
			else if (ast.get().get().compare("addr") == 0)
				rtn = LiteralType(LiteralType::_addr, compileAtomicType(ast.getBranches()[0], file).get());
			else if (ast.get().get().compare("xi8") == 0)
				rtn = LiteralType(LiteralType::_addr, LiteralType::_char);
			else if (ast.get().get().compare("xi32") == 0)
				rtn = LiteralType(LiteralType::_addr, LiteralType::_int);
			else if (ast.get().get().compare("xf32") == 0)
				rtn = LiteralType(LiteralType::_addr, LiteralType::_double);
			else if (ast.get().get().compare("string") == 0)
				rtn = LiteralType(LiteralType::_addr, LiteralType::_char);
			else
			{
				error(ast.get(), file, _cfg["error-atomic-type-unknown"]);
				note("type", ast.get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return LiteralType();
			}

			if (!rtn.initialized())
			{
				error(ast.get(), file, _cfg["error-uninitializable"]);
				note("expression type", ast.get().get(), file);
			}

			Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());

			return rtn;
		}

		vm::LiteralInt CodeServer::compileAtomicInt(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (!ast.isLeaf())
			{
				error(ast.get(), file, _cfg["error-atomic-int-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return LiteralInt();
			}
			
			if (!isInteger(ast.get().get()))
			{
				error(ast.get(), file, _cfg["error-atomic-int-miscompiled"]);
				note("value", ast.get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return LiteralInt();
			}
			
			LiteralInt rtn = LiteralInt(atoi(ast.get().get().c_str()));
			if (!rtn.initialized())
			{
				error(ast.get(), file, _cfg["error-uninitializable"]);
				note("expression type", ast.get().get(), file);
			}
			Z_DEBUG_OUT(boolCfg["debug-compilation"], ast.get().get());
			return rtn;
		}

		vm::LiteralDouble CodeServer::compileAtomicDouble(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (!ast.isLeaf())
			{
				error(ast.get(), file, _cfg["error-atomic-double-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return LiteralDouble();
			}
			
			if (!isDouble(ast.get().get()))
			{
				error(ast.get(), file, _cfg["error-atomic-double-miscompiled"]);
				note("value", ast.get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return LiteralDouble();
			}
			
			LiteralDouble rtn = LiteralDouble(atof(ast.get().get().c_str()));
			if (!rtn.initialized())
			{
				error(ast.get(), file, _cfg["error-uninitializable"]);
				note("expression type", ast.get().get(), file);
			}
			Z_DEBUG_OUT(boolCfg["debug-compilation"], ast.get().get());
			return rtn;
		}

		vm::LiteralAddress CodeServer::compileAtomicAddress(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (!ast.isLeaf())
			{
				error(ast.get(), file, _cfg["error-atomic-address-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return LiteralAddress();
			}
			
			if (!isAddress(ast.get().get()))
			{
				error(ast.get(), file, _cfg["error-atomic-address-miscompiled"]);
				note("value", ast.get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return LiteralAddress();
			}
			
			unsigned int rtnv = 0;
			stringstream ss;
			ss << hex << ast.get().get().substr(2);
			ss >> rtnv;
			LiteralAddress rtn = LiteralAddress((void *)rtnv);
			if (!rtn.initialized())
			{
				error(ast.get(), file, _cfg["error-uninitializable"]);
				note("expression type", ast.get().get(), file);
			}
			Z_DEBUG_OUT(boolCfg["debug-compilation"], ast.get().get());
			return rtn;
		}

		vm::LiteralValue CodeServer::compileAtomicValue(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (!ast.isLeaf())
			{
				error(ast.get(), file, _cfg["error-atomic-value-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return LiteralValue();
			}

			if (isInteger(ast.get().get()))
			{
				LiteralValue rtn = LiteralValue(compileAtomicInt(ast, file));
				if (!rtn.initialized())
				{
					error(ast.get(), file, _cfg["error-uninitializable"]);
					note("expression type", ast.get().get(), file);
				}
				Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());
				return rtn;
			}
			else if (isDouble(ast.get().get()))
			{
				LiteralValue rtn = LiteralValue(compileAtomicDouble(ast, file));
				if (!rtn.initialized())
				{
					error(ast.get(), file, _cfg["error-uninitializable"]);
					note("expression type", ast.get().get(), file);
				}
				Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());
				return rtn;
			}
			else if (isAddress(ast.get().get()))
			{
				LiteralInt rtn = LiteralInt((int)compileAtomicAddress(ast, file).get());
				if (!rtn.initialized())
				{
					error(ast.get(), file, _cfg["error-uninitializable"]);
					note("expression type", ast.get().get(), file);
				}
				Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display() << " (as addr)");
				return rtn;
			}
			else if (isString(ast.get().get()))
			{
				string buf = ast.get().get().substr(1);
				buf = buf.substr(0, buf.size()-1);
				LiteralAddress rtn = LiteralAddress(strdup(buf.c_str()));
				if (!rtn.initialized())
				{
					error(ast.get(), file, _cfg["error-uninitializable"]);
					note("expression type", ast.get().get(), file);
				}
				Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display() << " (as addr of string)");
				return rtn;
			}
			else if (isSymbol(ast.get().get()))
			{
				LiteralValue rtn = LiteralValue(ast.get().get());
				if (!rtn.initialized())
				{
					error(ast.get(), file, _cfg["error-uninitializable"]);
					note("expression type", ast.get().get(), file);
				}
				Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display() << " (as variable)");
				return rtn;
			}
			else
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return LiteralValue();
			}
		}

		vm::MutableDefinition CodeServer::compileNew(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (ast.getBranches().size() != 2 && ast.getBranches().size() != 3)
			{
				error(ast.get(), file, _cfg["error-new-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return MutableDefinition();
			}

			if (ast.get().get().compare("new") != 0)
			{
				error(ast.get(), file, _cfg["error-new-miscompilation"]);
				note("expression type", ast.get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return MutableDefinition();
			}

			if (ast.getBranches().size() == 3)
			{
				LiteralType t = compileAtomicType(ast.getBranches()[0], file);
				if (!t.initialized())
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return MutableDefinition();
				}

				string n = ast.getBranches()[1].get().get();

				FunctionalNode v = compileFunctionalNode(ast.getBranches()[2], file);
				if (!v.initialized())
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return MutableDefinition();
				}

				LiteralType vt = proofOfType(v);
				if (vt.get() != LiteralType::_int && vt.get() != t.get())
				{
					error(ast.get(), file, _cfg["error-mismatched-types"]);
					note("cannot cast", vt.display(), file);
					note("to", t.display(), file);
					return MutableDefinition();
				}

				MutableDefinition rtn = MutableDefinition(t, n, v);
				if (!rtn.initialized())
				{
					error(ast.get(), file, _cfg["error-uninitializable"]);
					note("expression type", ast.get().get(), file);
				}
				Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());
				if (mutables.find(n) != mutables.end())
				{
					error(ast.get(), file, _cfg["error-new-redefinition"]);
					note("mutable", n, file);
					return MutableDefinition();
				}
				mutables[n] = t;
				return rtn;
			}
			else if (ast.getBranches().size() == 2)
			{
				LiteralType t = compileAtomicType(ast.getBranches()[0], file);
				if (!t.initialized())
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return MutableDefinition();
				}

				string n = ast.getBranches()[1].get().get();

				MutableDefinition rtn = MutableDefinition(t, n);
				if (!rtn.initialized())
				{
					error(ast.get(), file, _cfg["error-uninitializable"]);
					note("expression type", ast.get().get(), file);
				}
				Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());
				if (mutables.find(n) != mutables.end())
				{
					error(ast.get(), file, _cfg["error-new-redefinition"]);
					note("mutable", n, file);
					return MutableDefinition();
				}
				mutables[n] = t;
				return rtn;
			}
		}

		vm::MutableAssignment CodeServer::compileAssign(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (ast.getBranches().size() != 2)
			{
				error(ast.get(), file, _cfg["error-assign-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return MutableAssignment();
			}

			if (ast.get().get().compare("=") != 0)
			{
				error(ast.get(), file, _cfg["error-assign-miscompile"]);
				note("expression type", ast.get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return MutableAssignment();
			}

			string n = ast.getBranches()[0].get().get();

			FunctionalNode v = compileFunctionalNode(ast.getBranches()[1], file);
			if (!v.initialized())
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return MutableAssignment();
			}

			if (mutables.find(n) != mutables.end())
			{
				LiteralType vt = proofOfType(v);
				if (vt.get() != LiteralType::_int && vt.get() != mutables[n].get())
				{
					error(ast.get(), file, _cfg["error-mismatched-types"]);
					note("cannot cast", vt.display(), file);
					note("to", mutables[n].display(), file);
					return MutableAssignment();
				}
			}

			MutableAssignment rtn = MutableAssignment(n, v);
			if (!rtn.initialized())
			{
				error(ast.get(), file, _cfg["error-uninitializable"]);
				note("expression type", ast.get().get(), file);
			}
			Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());
			return rtn;
		}

		vm::Operation CodeServer::compileOperation(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			FunctionalNode left = FunctionalNode();
			FunctionalNode right = FunctionalNode();
			
			if (ast.getBranches().size() == 2)
			{
				left = compileFunctionalNode(ast.getBranches()[0], file);
				if (!left.initialized())
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return Operation();
				}

				right = compileFunctionalNode(ast.getBranches()[1], file);
				if (!right.initialized())
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return Operation();
				}
			}
			else if (ast.getBranches().size() == 1)
			{
				right = compileFunctionalNode(ast.getBranches()[0], file);
				if (!right.initialized())
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return Operation();
				}
			}
			else
			{
				error(ast.get(), file, _cfg["error-operation-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return Operation();
			}

			int op = Operation::uninitialized;
			bool d = proofOfType(left).get() == LiteralType::_double || proofOfType(right).get() == LiteralType::_double;

			vm::FunctionalNode lc = vm::FunctionalNode();
			vm::FunctionalNode rc = vm::FunctionalNode();
			if (boolCfg["enable-autocasting"])
			{
				if (d)
				{
					if (left.initialized())
						lc = vm::Operation(vm::Operation::castf32, left);
					rc = vm::Operation(vm::Operation::castf32, right);
				}
				else
				{
					if (left.initialized())
						lc = vm::Operation(vm::Operation::casti32, left);
					rc = vm::Operation(vm::Operation::casti32, right);
				}
			}
			else
			{
				lc = left;
				rc = right;
			}

			if (!d)
			{
				if (ast.get().get().compare("+") == 0)
					op = Operation::iadd;
				else if (ast.get().get().compare("-") == 0)
					op = Operation::isub;
				else if (ast.get().get().compare("*") == 0)
					op = Operation::imul;
				else if (ast.get().get().compare("/") == 0)
					op = Operation::idiv;
				else if (ast.get().get().compare("&") == 0)
					op = Operation::iand;
				else if (ast.get().get().compare("|") == 0)
					op = Operation::ior;
				else if (ast.get().get().compare("^") == 0)
					op = Operation::ixor;
				else if (ast.get().get().compare("<") == 0)
					op = Operation::ilt;
				else if (ast.get().get().compare(">") == 0)
					op = Operation::igt;
				else if (ast.get().get().compare("<=") == 0)
					op = Operation::ile;
				else if (ast.get().get().compare(">=") == 0)
					op = Operation::ige;
				else if (ast.get().get().compare("==") == 0)
					op = Operation::ieq;
				else if (ast.get().get().compare("!=") == 0)
					op = Operation::ine;
				else if (ast.get().get().compare("!") == 0)
					op = Operation::ineg;
				else if (ast.get().get().compare("i8") == 0)
					op = Operation::casti8;
				else if (ast.get().get().compare("i32") == 0)
					op = Operation::casti32;
				else if (ast.get().get().compare("f32") == 0)
					op = Operation::castf32;
				else if (ast.get().get().compare("xi8") == 0)
					op = Operation::castxi8;
				else if (ast.get().get().compare("xi32") == 0)
					op = Operation::castxi32;
				else if (ast.get().get().compare("xf32") == 0)
					op = Operation::castxf32;
				else if (ast.get().get().compare("char") == 0)
					op = Operation::casti8;
				else if (ast.get().get().compare("int") == 0)
					op = Operation::casti32;
				else if (ast.get().get().compare("double") == 0)
					op = Operation::castf32;
				else if (ast.get().get().compare(":") == 0)
				{
					LiteralType rt = proofOfType(right);
					if (rt.get() != LiteralType::_addr)
					{
						error(ast.get(), file, _cfg["error-dereferencing-non-pointer"]);
						note("type", rt.display(), file);
						Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
						return Operation();
					}

					if (rt.getElement() == LiteralType::_char)
						op = Operation::derefxi8;
					else if (rt.getElement() == LiteralType::_int)
						op = Operation::derefxi32;
					else if (rt.getElement() == LiteralType::_double)
						op = Operation::derefxf32;
					else
					{
						error(ast.get(), file, _cfg["error-unsupported-dereference"]);
						note("type", rt.display(), file);
						Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
						return Operation();
					}
				}
				else if (ast.get().get().compare(".") == 0)
					op = Operation::xaddr;
				else
				{
					error(ast.get(), file, _cfg["error-operation-unknown-ints"]);
					note("operator", ast.get().get(), file);
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return Operation();
				}
			}
			else
			{
				if (ast.get().get().compare("+") == 0)
					op = Operation::fadd;
				else if (ast.get().get().compare("-") == 0)
					op = Operation::fsub;
				else if (ast.get().get().compare("*") == 0)
					op = Operation::fmul;
				else if (ast.get().get().compare("/") == 0)
					op = Operation::fdiv;
				else if (ast.get().get().compare("<") == 0)
					op = Operation::flt;
				else if (ast.get().get().compare(">") == 0)
					op = Operation::fgt;
				else if (ast.get().get().compare("<=") == 0)
					op = Operation::fle;
				else if (ast.get().get().compare(">=") == 0)
					op = Operation::fge;
				else if (ast.get().get().compare("==") == 0)
					op = Operation::feq;
				else if (ast.get().get().compare("!=") == 0)
					op = Operation::fne;
				else if (ast.get().get().compare("i8") == 0)
					op = Operation::casti8;
				else if (ast.get().get().compare("i32") == 0)
					op = Operation::casti32;
				else if (ast.get().get().compare("f32") == 0)
					op = Operation::castf32;
				else if (ast.get().get().compare("xi8") == 0)
					op = Operation::castxi8;
				else if (ast.get().get().compare("xi32") == 0)
					op = Operation::castxi32;
				else if (ast.get().get().compare("xf32") == 0)
					op = Operation::castxf32;
				else if (ast.get().get().compare("char") == 0)
					op = Operation::casti8;
				else if (ast.get().get().compare("int") == 0)
					op = Operation::casti32;
				else if (ast.get().get().compare("double") == 0)
					op = Operation::castf32;
				else if (ast.get().get().compare("xi8") == 0)
					op = Operation::castxi8;
				else if (ast.get().get().compare("xi32") == 0)
					op = Operation::castxi32;
				else if (ast.get().get().compare("xf32") == 0)
					op = Operation::castxf32;
				else
				{
					error(ast.get(), file, _cfg["error-operation-unknown-doubles"]);
					note("operator", ast.get().get(), file);
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return Operation();
				}
			}
			// else
			// {
			// 	error(ast.get(), file, _cfg["error-operation-unknown-type"]);
			// 	note("typeid", type, file);
			// 	if (boolCfg["debug-compilation"])
			// 		cout << "debug: compileOperation => <uninitialized>\n";
			// 	return Operation();
			// }

			if (ast.getBranches().size() == 2)
			{
				Operation rtn = Operation(lc, op, rc);
				if (!rtn.initialized())
				{
					error(ast.get(), file, _cfg["error-uninitializable"]);
					note("expression type", ast.get().get(), file);
				}
				Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());
				return rtn;
			}
			else if (ast.getBranches().size() == 1)
			{
				Operation rtn = Operation(op, rc);
				if (!rtn.initialized())
				{
					error(ast.get(), file, _cfg["error-uninitializable"]);
					note("expression type", ast.get().get(), file);
				}
				Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());
				return rtn;
			}
			else
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return Operation();
			}
		}

		vm::Call CodeServer::compileCall(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (!isSymbol(ast.get().get()))
			{
				error(ast.get(), file, _cfg["error-call-symbol"]);
				note("called function", ast.get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return Call();
			}

			vector<FunctionalNode> args;
			for (int i = 0; i < ast.getBranches().size(); i++)
			{
				FunctionalNode tmp = compileFunctionalNode(ast.getBranches()[i], file);
				if (!tmp.initialized())
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return Call();
				}

				LiteralType tmpt = proofOfType(tmp);
				if (tmpt.get() != LiteralType::_int && tmpt.get() != protos[ast.get().get()].getArgumentType(i).get())
				{
					error(ast.get(), file, _cfg["error-mismatched-types"]);
					note("type is", tmpt.display(), file);
					note("should be", protos[ast.get().get()].getArgumentType(i).display(), file);
					return Call();
				}

				args.push_back(tmp);
			}
			
			Call rtn = Call(ast.get().get(), args);
			if (!rtn.initialized())
			{
				error(ast.get(), file, _cfg["error-uninitializable"]);
				note("expression type", ast.get().get(), file);
			}

			Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());
			return rtn;
		}

		vm::IfStatement CodeServer::compileIfStatement(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (ast.get().get().compare("if") != 0)
			{
				error(ast.get(), file, _cfg["error-if-miscompiled"]);
				note("expression type", ast.get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return IfStatement();
			}
			
			if (ast.getBranches().size() != 5)
			{
				error(ast.get(), file, _cfg["error-if-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return IfStatement();
			}
			
			if (ast.getBranches()[1].get().get().compare("then") != 0)
			{
				error(ast.get(), file, _cfg["error-if-then"]);
				note("then statement type", ast.getBranches()[1].get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return IfStatement();
			}
			
			if (ast.getBranches()[3].get().get().compare("else") != 0)
			{
				error(ast.get(), file, _cfg["error-if-else"]);
				note("else statement type", ast.getBranches()[3].get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return IfStatement();
			}
			
			FunctionalNode c = compileFunctionalNode(ast.getBranches()[0], file);
			if (!c.initialized())
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return IfStatement();
			}

			FunctionalNode t = compileFunctionalNode(ast.getBranches()[2], file);
			if (!t.initialized())
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return IfStatement();
			}

			FunctionalNode e = compileFunctionalNode(ast.getBranches()[4], file);
			if (!e.initialized())
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return IfStatement();
			}

			IfStatement rtn = IfStatement(c, t, e);
			if (!rtn.initialized())
			{
				error(ast.get(), file, _cfg["error-uninitializable"]);
				note("expression type", ast.get().get(), file);
			}
			Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());
			return rtn;
		}

		vm::ForLoop CodeServer::compileForLoop(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (ast.get().get().compare("for") != 0)
			{
				error(ast.get(), file, _cfg["error-for-miscompiled"]);
				note("expression type", ast.get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return ForLoop();
			}

			if (ast.getBranches().size() != 4)
			{
				error(ast.get(), file, _cfg["error-for-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return ForLoop();
			}

			if (ast.getBranches()[0].get().get().compare("new") != 0)
			{
				error(ast.get(), file, _cfg["error-for-init"]);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return ForLoop();
			}

			if (ast.getBranches()[0].getBranches().size() != 2)
			{
				error(ast.get(), file, _cfg["error-for-init-arguments"]);
				note("argument size", ast.getBranches()[0].getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return ForLoop();
			}

			FunctionalNode i = compileFunctionalNode(ast.getBranches()[0].getBranches()[1], file);
			if (!i.initialized())
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return ForLoop();
			}

			FunctionalNode e = compileFunctionalNode(ast.getBranches()[1], file);
			if (!e.initialized())
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return ForLoop();
			}
			
			FunctionalNode s = compileFunctionalNode(ast.getBranches()[2], file);
			if (!s.initialized())
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return ForLoop();
			}
			
			FunctionalNode b = compileFunctionalNode(ast.getBranches()[3], file);
			if (!b.initialized())
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return ForLoop();
			}

			ForLoop rtn = ForLoop(ast.getBranches()[0].getBranches()[0].get().get(), i, e, s, b);
			if (!rtn.initialized())
			{
				error(ast.get(), file, _cfg["error-uninitializable"]);
				note("expression type", ast.get().get(), file);
			}
			Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());
			return rtn;
		}

		vm::FunctionalNode CodeServer::compileFunctionalNode(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			FunctionalNode rtn;

			if (ast.get().get().compare("for") == 0)
			{
				rtn = FunctionalNode(compileForLoop(ast, file));
			}
			else if (ast.get().get().compare("if") == 0)
			{
				rtn = FunctionalNode(compileIfStatement(ast, file));
			}
			else if (ast.get().get().compare("new") == 0)
			{
				rtn = FunctionalNode(compileNew(ast, file));
			}
			else if (ast.get().get().compare("=") == 0)
			{
				rtn = FunctionalNode(compileAssign(ast, file));
			}
			else if (isOperation(ast.get().get()) && !ast.isLeaf())
			{
				rtn = FunctionalNode(compileOperation(ast, file));
			}
			else if (isFunction(ast.get().get()))
			{
				rtn = FunctionalNode(compileCall(ast, file));
			}
			else if (isValue(ast.get().get()) && ast.isLeaf())
			{
				rtn = FunctionalNode(compileAtomicValue(ast, file));
			}
			else
			{
				error(ast.get(), file, _cfg["error-functional-node-unknown"]);
				note("expression type", ast.get().get(), file);
				note("can be caused by undeclared functions, unknown operators, ...", file);
				return FunctionalNode();
			}

			Z_DEBUG_OUT(boolCfg["debug-compilation"], rtn.display());
			return rtn;
		}

		vm::Prototype CodeServer::compilePrototype(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (ast.get().get().compare("decl") != 0)
			{
				error(ast.get(), file, _cfg["error-decl-miscompiled"]);
				note("expression type", ast.get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return Prototype();
			}

			if (ast.getBranches().size() < 2)
			{
				error(ast.get(), file, _cfg["error-decl-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return Prototype();
			}

			LiteralType r = compileAtomicType(ast.getBranches()[0], file);
			if (!r.initialized())
			{
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return Prototype();
			}

			string n = ast.getBranches()[1].get().get();
			vector<pair<LiteralType, string> > args;

			for (int i = 2; i < ast.getBranches().size(); i++)
			{
				pair<LiteralType, string> tmp;
				tmp.first = compileAtomicType(AST(ast.getBranches()[i].get()), file);
				if (!tmp.first.initialized())
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return Prototype();
				}

				tmp.second = ast.getBranches()[i].getBranches()[0].get().get();
				args.push_back(tmp);
			}

			Prototype p = Prototype(n, r, args);
			if (protos.find(n) != protos.end())
			{
				error(ast.get(), file, _cfg["error-decl-redeclaration"]);
				note("function", n, file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return Prototype();
			}
			protos[n] = p;
			Z_DEBUG_OUT(boolCfg["debug-compilation"], p.display());
			return p;
		}

		vm::Body CodeServer::compileBody(AST ast, string file)
		{
			Z_DEBUG_IN(boolCfg["debug-compilation"], ast.display());

			if (ast.get().get().compare("def") != 0)
			{
				error(ast.get(), file, _cfg["error-def-miscompiled"]);
				note("expression type", ast.get().get(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return Body();
			}

			if (ast.getBranches().size() < 2)
			{
				error(ast.get(), file, _cfg["error-def-arguments"]);
				note("argument size", ast.getBranches().size(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return Body();
			}

			Prototype p;

			if (ast.getBranches()[0].isLeaf())
			{
				p = protos[ast.getBranches()[0].get().get()];
			}
			else
			{
				p = compilePrototype(ast.getBranches()[0], file);
				if (!p.initialized())
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return Body();
				}
			}

			Body b = Body(p);
			for (int i = 1; i < ast.getBranches().size(); i++)
			{
				FunctionalNode tmp = compileFunctionalNode(ast.getBranches()[i], file);
				if (!tmp.initialized())
				{
					Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
					return Body();
				}
				b.appendFunctionalNode(tmp);
			}

			if (funcs.find(p.getName()) != funcs.end())
			{
				error(ast.get(), file, _cfg["error-def-redefinition"]);
				note("function", p.getName(), file);
				Z_DEBUG_OUT(boolCfg["debug-compilation"], "<uninitialized>");
				return Body();
			}

			funcs[p.getName()] = b;
			asts[p.getName()] = ast;
			Z_DEBUG_OUT(boolCfg["debug-compilation"], b.display());
			return b;
		}

		pair<string, void *> CodeServer::compileMain(AST ast, string file, vm::VM &vm)
		{
			Body b = compileBody(ast, file);
			pair<string, void *> rtn;
			rtn.second = NULL;
			if (!b.initialized())
				return rtn;
			rtn.first = b.getPrototype().getName();
			rtn.second = vm.JIT(b);
			return rtn;
		}

		void CodeServer::initCfg()
		{
			_cfg.clear();

			_cfg["main-symbol"] = "main";
			_cfg["import-path"] = ".:..";

			_cfg["error-mismatched-types"] = "mismatched types - harness your anger and frustration and put something in a cast";
			_cfg["error-macro-arguments"] = "unexpected number of arguments for macro";
			_cfg["error-improper-macro"] = "improper macro definition";
			_cfg["error-atomic-type-arguments"] = "atomic types don't take arguments";
			_cfg["error-atomic-type-unknown"] = "unknown atomic type (there's just 'int' and 'double')";
			_cfg["error-atomic-int-arguments"] = "atomic ints don't take arguments (it's weird that this error got this deep into the workin's)";
			_cfg["error-atomic-int-miscompiled"] = "something that is not an atomic int is being compiled as an atomic int (it's weird that this error got this deep into the workin's)";
			_cfg["error-atomic-double-arguments"] = "atomic doubles don't take arguments (it's weird that this error got this deep into the workin's)";
			_cfg["error-atomic-double-miscompiled"] = "something that is not an atomic double is being compiled as an atomic double (it's weird that this error got this deep into the workin's)";
			_cfg["error-atomic-address-arguments"] = "atomic addresses don't take arguments (it's weird that this error got this deep into the workin's)";
			_cfg["error-atomic-address-miscompiled"] = "something that is not an atomic address is being compiled as an atomic double (it's weird that this error got this deep into the workin's)";
			_cfg["error-atomic-value-arguments"] = "atomic values don't take arguments";
			_cfg["error-new-arguments"] = "variable definitions only take 3 arguments (sometimes 2)";
			_cfg["error-new-miscompilation"] = "something that is not a variable definition is being compiled as a variable definition (it's weird that this error got this deep into the workin's)";
			_cfg["error-new-redefinition"] = "redefinition of variable - ain't nobody got time for that";
			_cfg["error-assign-arguments"] = "variable assignments only take 2 arguments";
			_cfg["error-assign-miscompile"] = "something that is not a variable assignment is being compiled as a variable assignment (it's weird that this error got this deep into the workin's)";
			_cfg["error-operation-arguments"] = "operations only take 2 arguments (1 for some operations)";
			_cfg["error-operation-unknown-ints"] = "unknown operator on integer(s)";
			_cfg["error-operation-unknown-doubles"] = "unknown operator on double(s)";
			_cfg["error-operation-unknown-type"] = "unknown type for operations (it's weird that this error got this deep into the workin's)";
			_cfg["error-dereferencing-non-pointer"] = "cannot dereference something that is not a pointer (you have angered the pointer gods)";
			_cfg["error-unsupported-dereference"] = "cannot dereference that particular type - it's not supported (you have angered the pointer gods)";
			_cfg["error-call-symbol"] = "you can't call something that's not a symbol - wrong number (it's weird that this error got this deep into the workin's)";
			_cfg["error-if-miscompiled"] = "something that is not an if statement is being compiled as an if statement (it's weird that this error got this deep into the workin's)";
			_cfg["error-if-arguments"] = "not enough arguments for if statement - must be of format (if <condition expression> then <then expression> else <else expression>)";
			_cfg["error-if-then"] = "second argument for if statement should be 'then' - must be of format (if <condition expression> then <then expression> else <else expression>)";
			_cfg["error-if-else"] = "second argument for if statement should be 'else' - must be of format (if <condition expression> then <then expression> else <else expression>)";
			_cfg["error-for-miscompiled"] = "something that is not a for loop is being compiled as a for loop (it's weird that this error got this deep into the workin's)";
			_cfg["error-for-arguments"] = "not enough arguments for for loop - must be of format (for (new <variable name> <initial value>) <step expression> <end expression> <loop body>)";
			_cfg["error-for-init"] = "first argument for for loop should be of format (new <type> <name>) - must be of format (for (new <variable name> <initial value>) <step expression> <end expression> <loop body>)";
			_cfg["error-for-init-arguments"] = "first argument for for loop should be of format (new <type> <name>) - must be of format (for (new <variable name> <initial value>) <step expression> <end expression> <loop body>)";
			_cfg["error-functional-node-unknown"] = "unknown expression type";
			_cfg["error-decl-miscompiled"] = "something that is not a declaration is being compiled as a declaration (it's weird that this error got this deep into the workin's)";
			_cfg["error-decl-arguments"] = "not enough arguments for declaration - must be of format (decl <function type> <function name> (<first argument type> <first argument name>) ...)";
			_cfg["error-decl-redeclaration"] = "redeclaration of existing prototype - ain't nobody got time for that";
			_cfg["error-def-miscompiled"] = "something that is not a definition is being compiled as a definition (it's weird that this error got this deep into the workin's)";
			_cfg["error-def-arguments"] = "not enough arguments for definition - must be of format (def <prototype or prototype name> <first line> ...)";
			_cfg["error-def-redefinition"] = "redefinition of existing function - ain't nobody got time for that";
			_cfg["error-main-redefinition"] = "redefinition of existing main function - there's only room in this town for one";
			_cfg["error-failed-compilation-parsing"] = "couldn't compile the code (parsing stage) - i have failed you";
			_cfg["error-failed-compilation-jit"] = "couldn't compile the code (jit stage) - i have failed you";
			_cfg["error-uninitializable"] = "could not verifiably initialize compilation element (it's weird that this error got this deep into the workin's)";

			_cfg["debug-parsing"] = "0";
			_cfg["debug-compilation"] = "0";
			_cfg["debug-llvm-module"] = "0";

			_cfg["enable-autocasting"] = "1";

			updateImportPath();
			addBoolCfg("debug-parsing");
			addBoolCfg("debug-compilation");
			addBoolCfg("debug-llvm-module");
			addBoolCfg("enable-autocasting");
			updateBoolCfg();
		}

		void CodeServer::initReserved()
		{
			ops.push_back("+");
			ops.push_back("-");
			ops.push_back("*");
			ops.push_back("/");
			ops.push_back("&");
			ops.push_back("|");
			ops.push_back("^");
			ops.push_back("<");
			ops.push_back(">");
			ops.push_back("<=");
			ops.push_back(">=");
			ops.push_back("==");
			ops.push_back("!=");
			ops.push_back("!");
			ops.push_back("char");
			ops.push_back("int");
			ops.push_back("double");
			ops.push_back("i8");
			ops.push_back("i32");
			ops.push_back("f32");
			ops.push_back("xi8");
			ops.push_back("xi32");
			ops.push_back("xf32");
		}

		Body CodeServer::createWrapperFunction(string name, FunctionalNode node)
		{
			vector<pair<LiteralType, string> > tmp;
			Prototype p= Prototype(name, proofOfType(node), tmp);
			protos[name] = p;
			Body b = Body(p);
			funcs[name] = b;
			b.appendFunctionalNode(node);
			return b;
		}

		LiteralValue CodeServer::executeFunction(string name, void *ptr)
		{
			LiteralType t = protos[name].getReturnType();
			if (t.get() == LiteralType::_char)
				return LiteralValue(LiteralChar(((char(*)())ptr)()));
			else if (t.get() == LiteralType::_int)
				return LiteralValue(LiteralInt(((int(*)())ptr)()));
			else if (t.get() == LiteralType::_double)
				return LiteralValue(LiteralDouble(((double(*)())ptr)()));
			else if (t.get() == LiteralType::_addr)
				return LiteralValue(LiteralAddress(((void *(*)())ptr)()));
			else
				return LiteralValue();
		}

		CodeServer::CodeServer()
		{
			importPathLexer = Lexer(":", ":", "", "");
			initCfg();
			initReserved();
			lexer = Lexer(" \n\t()", " \n\t", "\"'", "\"'");
			parser = Parser("(", ")");
		}

		CodeServer::CodeServer(string whitespace, char leftBracket, char rightBracket, string leftQuotes, string rightQuotes)
		{
			importPathLexer = Lexer(":", ":", "", "");
			initCfg();
			initReserved();
			string lb;
			lb += leftBracket;
			string rb;
			rb += rightBracket;
			lexer = Lexer(whitespace+lb+rb, whitespace, leftQuotes, rightQuotes);
			parser = Parser(lb, rb);
		}

		CodeServer::~CodeServer()
		{
		}

		bool CodeServer::getDebugLLVMModule()
		{
			return boolCfg["debug-llvm-module"];
		}

		int CodeServer::macro(string name, AST body)
		{
			if (directMacros.find(name) != directMacros.end())
			{
				Z_WARNING("redefinition of macro: " << name);
				return 1;
			}
			directMacros[name] = body;
			return 0;
		}

		int CodeServer::macro(string name, vector<string> args, AST body)
		{
			if (functionalMacros.find(name) != functionalMacros.end())
			{
				Z_WARNING("redefinition of macro: " << name);
				return 1;
			}

			functionalMacros[name] = FunctionalMacro(name, body);
			for (vector<string>::iterator i = args.begin(); i != args.end(); i++)
				functionalMacros[name].addArgument(*i);
			return 0;
		}

		AST CodeServer::getFunctionAST(string name)
		{
			if (asts.find(name) == asts.end())
				return AST();
			return asts[name];
		}

		string CodeServer::cfg(string k)
		{
			if (_cfg.find(k) != _cfg.end())
				return _cfg[k];
			else
				return "";
		}

		string CodeServer::cfg(string k, string v)
		{
			_cfg[k] = v;
			if (k.compare("import-path") == 0)
				updateImportPath();
			if (boolCfg.find(k) != boolCfg.end())
				updateBoolCfg();
			return v;
		}

		void *CodeServer::compile(string path, VM &vm)
		{
			ast = loadAndParse(path, lexer, parser, importPath);

			vector<AST> lines;

			if (ast.get().get().empty())
				lines = ast.getBranches();
			else
				lines.push_back(ast);

			void *rtn = NULL;
			for (vector<AST>::iterator i = lines.begin(); i != lines.end(); i++)
			{
				pair<string, void *> tmp = compileMain(*i, path, vm);
				if (tmp.first.compare(_cfg["main-symbol"]) == 0)
				{
					if (rtn != NULL)
					{
						error(i->get(), path, _cfg["error-main-redefinition"]);
						note("main symbol", _cfg["main-symbol"], path);
					}
					else
					{
						rtn = tmp.second;
					}
				}
			}

			exitIfErrors();
			return rtn;
		}

		void CodeServer::shell(VM &vm)
		{
			int cid = 0;

			while(1)
			{
				char *cline = readline("opal% ");
				if (cline == NULL)
				{
					Z_WARNING("internal error in readline");
					break;
				}
				string line = string(cline);
				if (line.empty())
					continue;

				add_history(line.c_str());
				AST cmd = parseAST(line, "--");

				if (cmd.get().get().compare("exit") == 0)
				{
					if (!cmd.isLeaf())
						Z_WARNING("exit doesn't take any arguments");
					break;
				}
				else if (cmd.get().get().compare("cfg") == 0)
				{
					if (cmd.isLeaf())
					{
						int max = 0;
						for (map<string, string>::iterator i = _cfg.begin(); i != _cfg.end(); i++)
						{
							if (i->first.size() > max)
								max = i->first.size();
						}

						for (map<string, string>::iterator i = _cfg.begin(); i != _cfg.end(); i++)
						{
							cout << "\"" << i->first << "\"";
							for (int j = 0; j < max-i->first.size(); j++)
								cout << " ";
							cout << " == \"" << i->second << "\"\n";
						}
					}
					else if (cmd.getBranches().size() == 1)
					{
						if (!cmd.getBranches()[0].isLeaf())
							Z_WARNING("cfg argument doesn't take any arguments");
						cout << "\"" << cfg(cmd.getBranches()[0].get().get()) << "\"\n";
					}
					else if (cmd.getBranches().size() == 2)
					{
						if (!cmd.getBranches()[0].isLeaf() || !cmd.getBranches()[1].isLeaf())
							Z_WARNING("cfg argument doesn't take any arguments");
						cout << "\"" << cmd.getBranches()[0].get().get() << "\" <= \"" << cmd.getBranches()[1].get().get() << "\"\n";
						cfg(cmd.getBranches()[0].get().get(), cmd.getBranches()[1].get().get());
					}
					else
					{
						Z_WARNING("too many arguments for cfg");
					}
				}
				else if (cmd.get().get().compare("help") == 0)
				{
					if (cmd.isLeaf())
					{
						cout << "exit - exits the shell\n";
						cout << "cfg  - retrieves/modifies configuration data for code server\n";
						cout << "help - displays this help\n";
						cout << "decl - declare prototype\n";
						cout << "def  - define function\n";
						cout << "lsjit - list JITed functions\n";
						cout << "unjit - unJIT function\n";
					}
					else if (cmd.getBranches().size() == 1)
					{
						if (!cmd.getBranches()[0].isLeaf())
							Z_WARNING("help argument doesn't take any arguments");

						if (cmd.getBranches()[0].get().get().compare("exit") == 0)
						{
							cout << "(exit) - exits the shell\n";
						}
						else if (cmd.getBranches()[0].get().get().compare("cfg") == 0)
						{
							cout << "(cfg)                    - displays all configuration variables\n";
							cout << "(cfg <VARIABLE>)         - displays configuration variable <VARIABLE>\n";
							cout << "(cfg <VARIABLE> <VALUE>) - sets configuration variable <VARIABLE> to <VALUE>\n";
						}
						else if (cmd.getBranches()[0].get().get().compare("help") == 0)
						{
							cout << "(help)           - lists summaries of all commands\n";
							cout << "(help <COMMAND>) - displays specific help for <COMMAND>\n";
						}
						else if (cmd.getBranches()[0].get().get().compare("decl") == 0)
						{
							cout << "(decl) - lists declared prototypes\n";
							cout << "otherwise acts like opal construct\n";
						}
						else if (cmd.getBranches()[0].get().get().compare("def") == 0)
						{
							cout << "(def) - lists defined functions\n";
							cout << "otherwise acts like opal construct\n";
						}
						else if (cmd.getBranches()[0].get().get().compare("lsjit") == 0)
						{
							cout << "(lsjit) - lists JITed functions\n";
						}
						else if (cmd.getBranches()[0].get().get().compare("unjit") == 0)
						{
							cout << "(unjit <FUNCTION>) - unJITs function (releases memory)\n";
						}
						else
						{
							Z_WARNING("unknown command: " << cmd.getBranches()[0].get().get());
						}
					}
				}
				else if (cmd.get().get().compare("decl") == 0)
				{
					if (cmd.isLeaf())
					{
						for (map<string, Prototype>::iterator i = protos.begin(); i != protos.end(); i++)
							cout << i->first << "\n";
					}
					else
					{
						Prototype p = compilePrototype(cmd, "--");
						dumpErrors();
					}
				}
				else if (cmd.get().get().compare("def") == 0)
				{
					if (cmd.isLeaf())
					{
						for (map<string, Body>::iterator i = funcs.begin(); i != funcs.end(); i++)
							cout << i->first << "\n";
					}
					else
					{
						Body b = compileBody(cmd, "--");
						if (!dumpErrors())
							vm.JIT(b, boolCfg["debug-llvm-module"]);
					}
				}
				else if (cmd.get().get().compare("lsjit") == 0)
				{
					if (!cmd.isLeaf())
						Z_WARNING("lsjit doesn't take any arguments");

					vector<string> l = vm.listJITedFunctions();
					for (vector<string>::iterator i = l.begin(); i != l.end(); i++)
						cout << *i << "\n";
				}
				else if (cmd.get().get().compare("unjit") == 0)
				{
					if (cmd.isLeaf())
					{
						Z_WARNING("unjit requires the name of the function to unjit as an argument");
						continue;
					}

					if (cmd.getBranches().size() > 1)
					{
						Z_WARNING("too many arguments for unjit");
						continue;
					}

					int e = vm.unJIT(cmd.getBranches()[0].get().get());
					if (e == 1)
					{
						Z_WARNING("no such function: " << cmd.getBranches()[0].get().get());
					}
					else if (e == 2)
					{
						Z_WARNING("function already unjitted: " << cmd.getBranches()[0].get().get());
					}
				}
				else
				{
					FunctionalNode fn = compileFunctionalNode(cmd, "--");
					if (!fn.initialized())
					{
						error(cmd.get(), "--", _cfg["error-failed-compilation-parsing"]);
						note("expression typeid", fn.getType(), "--");
						dumpErrors();
					}
					else
					{
						stringstream ss;
						ss << "__shell_" << cid;
						Body tmp = createWrapperFunction(ss.str(), fn);
						void *f = vm.JIT(tmp, boolCfg["debug-llvm-module"]);
						if (f == NULL)
						{
							cout << tmp.display() << "\n";
							error(cmd.get(), "--", _cfg["error-failed-compilation-jit"]);
							dumpErrors();
						}
						else
						{
							LiteralValue res = executeFunction(ss.str(), f);
							cout << "[" << res.display() << "]\n";
						}
					}
				}

				cid++;
			}
		}

		void CodeServer::shellBackground(VM &vm)
		{
			pid_t pid = fork();

			if (pid == 0)
				shell(vm);

			wait(NULL);
		}
	}
}
